/*
    异步工作器
*/

#pragma once

#include "buffer.hpp"
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <functional>

namespace easylog
{
    
    //异步工作器
    class AsyncLooper
    {
    public:
        //回调函数类型
        using Functor = std::function<void(Buffer& )>;
        using ptr = std::shared_ptr<AsyncLooper>;
        //必须传入回调函数，扩容模式默认为阻塞
        AsyncLooper(const Functor& cb, ReserveType type)
                    :_stop(false),
                    _callBack(cb),
                    _thread(std::thread(&AsyncLooper::threadEntry, this))
                    { 
                        _pro_buf.changeIsAbleReserve(type);
                        _con_buf.changeIsAbleReserve(type);    
                    }
        ~AsyncLooper() { stop(); }
        //往缓冲区放数据
        void push(const char* data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            //确保有空间写数据
            _cond_pro.wait(lock, [&](){ return len <= _pro_buf.writeAbleSize(); });
            //写入数据
            _pro_buf.push(data, len);
            //唤醒消费者
            _cond_con.notify_one();
        }
        //停止异步工作器
        void stop()
        {
            _stop = true;//设置停止标记
            //唤醒所有工作线程
            _cond_con.notify_all();
            //等待线程退出
            _thread.join();
        }
    private:
        //线程入口函数
        void threadEntry()
        {
            while(1)
            {
                //为互斥锁创建一个局部的生存周期，只在交换缓冲区时加锁 
                {
                    //确保生产缓冲区有数据
                    std::unique_lock<std::mutex> lock(_mutex);
                    //只有当退出标志被设置，并且生产缓冲区为空才退出
                    if(_stop && _pro_buf.empty()) break;
                    //唤醒消费者的条件：1. 生产缓冲区有数据 2. 异步工作器停止，清空缓冲区
                    _cond_con.wait(lock, [&](){ return _stop || !_pro_buf.empty(); });
                    //交换缓冲区
                    _con_buf.swap(_pro_buf);
                    //唤醒生产者
                    _cond_pro.notify_all();
                }
                //执行回调函数，处理消费缓冲区数据
                _callBack(_con_buf);
                //重置消费缓冲区
                _con_buf.reset();
            }
        }
    private:
        Functor _callBack;//回调函数
    private:
        Buffer _pro_buf;//生产缓冲区
        Buffer _con_buf;//消费缓冲区
        std::thread _thread;//异步线程
        std::mutex _mutex;//互斥锁
        std::condition_variable _cond_pro;//生产条件变量
        std::condition_variable _cond_con;//消费条件变量
        std::atomic<bool> _stop;//停止标记
    };
}